package com.echronos.payment.service;

import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.echronos.commons.Result;
import com.echronos.commons.exception.BaseException;
import com.echronos.commons.exception.BusinessException;
import com.echronos.commons.model.AppThreadLocal;
import com.echronos.commons.model.BaseModel;
import com.echronos.commons.model.RequestUser;
import com.echronos.commons.utils.CopyObjectUtils;
import com.echronos.commons.utils.RequestUserUtils;
import com.echronos.finance.api.enums.CreditModeEnums;
import com.echronos.finance.api.enums.OrderTypeEnums;
import com.echronos.finance.api.feign.AccountBalanceFeignClient;
import com.echronos.finance.api.feign.CreditSettingFeignClient;
import com.echronos.finance.api.feign.ICreditConfigureFeignClient;
import com.echronos.finance.api.feign.ReceiptOrderFeignClient;
import com.echronos.finance.api.req.*;
import com.echronos.finance.api.resp.*;
import com.echronos.order.feign.IOrderFeign;
import com.echronos.order.resp.QueryWanZhongOrdDetailResp;
import com.echronos.payment.annotation.PayChannelInterfaceHandler;
import com.echronos.payment.commons.PayDictCode;
import com.echronos.payment.commons.ccb.enums.CcbPayCmdEnums;
import com.echronos.payment.dto.*;
import com.echronos.payment.enums.*;
import com.echronos.payment.hnc.model.HncMch;
import com.echronos.payment.hnc.service.HncMchService;
import com.echronos.payment.manager.*;
import com.echronos.payment.model.*;
import com.echronos.payment.param.*;
import com.echronos.payment.util.IpUtil;
import com.echronos.payment.util.NumberSequence;
import com.echronos.payment.util.RedisLock;
import com.echronos.payment.vo.*;
import com.echronos.user.api.resp.company.SimpleCompanyResp;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 统一支付
 *
 * @author wangyadong
 * @date 2021/2/1 20:10
 */
@Slf4j
@Service
@Transactional(readOnly = true, rollbackFor = Exception.class)
public class UnifiedPayService {
    @Resource
    private AliPayService aliPayService;
    @Resource
    private ReceiptOrderFeignClient receiptOrderFeignClient;
    @Resource
    private PayWayService payWayService;
    @Resource
    private AccountBalanceFeignClient balanceFeignClient;
    @Resource
    private ReceiptLogManager receiptLogManager;
    @Resource
    private AllinPayService allinPayService;
    @Resource
    private PaymentDictService dictService;
    @Resource
    private CreditSettingFeignClient creditSettingFeignClient;
    @Resource
    private WechatPayService wechatPayService;
    @Resource
    private PaymentPayLogManager payLogManager;
    @Resource
    private IPayAppInfoManager appInfoManager;
    @Resource
    private MerchantInfoManager merchantInfoManager;
    @Resource
    private MerchantSubManager subManager;

    @Resource
    private CcbPayService ccbPayService;

    @Resource
    private IOrderFeign orderFeign;

    @Resource
    private CcbSubMchBindService subMchBindService;
    @Resource
    private ICreditConfigureFeignClient creditConfigureFeignClient;
    @Resource
    private RedisLock redisLock;

    @Resource
    private PayModeSettingManager modeSettingManager;
    @Resource
    private PayModeManager payModeManager;

    @Resource
    private IPayHzPayService hzUnifiedPayService;
    /**
     * 超时时间 2S
     */
    private final long notifyOutTime = 2;
    /**
     * 订单过期任务
     */
    private static String lock_pay_commit_apply = "payment:pay:unifiedPay:";
    @Resource
    private HncMchService hncMchService;

    @Resource
    private SupportPaymentPayWayManager paymentPayWayManager;
    @Resource
    private CompanyService companyService;

    @Resource
    private SupportGatewayManager gatewayManager;

    @Resource
    private SupportCpcnMchManager supportCpcnMchManager;
    @Resource
    private SupportTenantPayWayManager tenantPayWayManager;
    @Value("${cpcn.bankTransFilter}")
    private String bankTransFilterCompanyIds;

    @Value("${cpcn.quickBankCardFilter}")
    private String quickBankCardFilterCompanyIds;

    /**
     * 根据收款单获取收银台信息
     *
     * @param param 收银台参数
     * @return CashDeskVo
     */
    @Transactional(readOnly = false)
    public Result<CashDeskVo> cashDesk(CashDeskParam param) {
        log.info("支付中心收银台收到请求参数param：{}", JSONObject.toJSONString(param));
        Integer companyId = RequestUserUtils.getUser().getCompanyId();
        log.info("当前公司的id:{}", companyId);
        CashDeskVo deskVo = new CashDeskVo();
        PaymentResultCode.PaymentResultEnum resultEnum = checkReceipt(param.getOrderNos());
        if (ObjectUtils.isNotEmpty(resultEnum)) {
            return Result.build(resultEnum.getCode(), resultEnum.getMessage());
        }
        String tenantId = StringUtils.isBlank(param.getTenantId()) ? AppThreadLocal.getTenantId() : param.getTenantId();
        //校验收款信息
        AcquireReceiptOnlineReq req = CopyObjectUtils.copyAtoB(param, AcquireReceiptOnlineReq.class);
        req.setPayCompanyId(companyId);
        ReceiptOnlineResp receipt = getReceiptByNos(req);
        if (ObjectUtils.isNull(param.getAmount())) {
            param.setAmount(receipt.getPayAmount());
        }
        log.info("生成的收款记录receipt:{}", JSONObject.toJSONString(receipt));
        List<Integer> receiptCompanyIds = receipt.getReceiptCompanyIds();
        if (ObjectUtils.isEmpty(receipt)) {
            return Result.build(PaymentResultCode.PaymentResultEnum.RECEIPT_NOT_EXISTS.getCode(),
                    PaymentResultCode.PaymentResultEnum.RECEIPT_NOT_EXISTS.getMessage());
        }
        List<SimpleCompanyResp> companyResps = Lists.newArrayList();
        if (CollectionUtils.isNotEmpty(receiptCompanyIds)) {
            companyResps = companyService.queryCompanyInfoByCompanyIds(receiptCompanyIds, null);
        }
        Integer toCompanyId = receipt.getToCompanyId() != 0 ? receipt.getToCompanyId() : null;
        //余额账户信息
        Map<Integer, AccountBalanceResp> account = getAccountBalance(companyId, receipt.getReceiptCompanyId(),
                receipt.getSourceCompanyId(), toCompanyId);
        log.info("在上级公司的余额账户account:{}", JSONObject.toJSONString(account));
        if (ObjectUtils.isEmpty(account)) {
            return Result.build(PaymentResultCode.PaymentResultEnum.ACCOUNT_NOT_EXISTS.getCode(),
                    PaymentResultCode.PaymentResultEnum.ACCOUNT_NOT_EXISTS.getMessage());
        }
        AccountBalanceResp balanceResp = account.get(AccountTypeEnums.NORMALLY_ACCOUNT.getCode());
        //收款方信息
        CashDeskReceiptInfo receiptInfo = CopyObjectUtils.copyAtoB(balanceResp, CashDeskReceiptInfo.class);
        receiptInfo.setAccountId(balanceResp.getId());
        //web收银台，中金支付的特殊处理(易梯和悦安居-中金支付通道的web收银台是华华的站点，这里转换成中金支付平台商户的站点)
        Boolean receiptIsOpenCpcn = false;
        Boolean tenantIsOpenCpcn = false;
        Boolean isPcDomain = DomainEnums.PC_DOMAIN.getCode().equals(param.getDomain());
        if (isPcDomain) {
            ReceiptTenantReturnDTO returnDTO = cpcnPayOfTenantConvert(receipt.getReceiptCompanyId(), tenantId);
            tenantId = returnDTO.getCurrTenantId();
            receiptIsOpenCpcn = returnDTO.getReceiptIsOpenCpcn();
            tenantIsOpenCpcn = returnDTO.getTenantIsOpenCpcn();
        }
        List<SupportPaymentPayWayDTO> payWayListByRoute = paymentPayWayManager.getPayWayListByRoute(tenantId);
        if (CollectionUtils.isNotEmpty(payWayListByRoute)) {
            List<SupportGateway> gateways = gatewayManager.list(new LambdaQueryWrapper<SupportGateway>()
                    .eq(SupportGateway::getIsEnable, true)
            );
            SupportGateway supportGateway = gateways.stream().findFirst().get();
            StringBuilder qrCodeUrl = new StringBuilder();
            qrCodeUrl.append(supportGateway.getGatewayUrl());
            qrCodeUrl.append(supportGateway.getQrPayRedirect());
            qrCodeUrl.append("?payOrderNo=").append(receipt.getPayOrderNo());
            qrCodeUrl.append("&payUserId=").append(RequestUserUtils.getUser().getId());
            qrCodeUrl.append("&tenantId=").append(tenantId);
            Map<String, List<SupportPaymentPayWayDTO>> payWayMap = payWayListByRoute.stream().collect(Collectors.
                    groupingBy(SupportPaymentPayWayDTO::getChannelCode));
            List<PayModeVo> payModes = Lists.newArrayList();
            payWayMap.entrySet().stream().forEach(r -> {
                PayModeVo modeVo = new PayModeVo();
                modeVo.setModeCode(r.getKey());
                ArrayList<PayWayVo> wayVos = Lists.newArrayList();
                r.getValue().stream().forEach(s -> {
                    PayWayVo wayVo = new PayWayVo();
                    wayVo.setPayWay(s.getName());
                    wayVo.setPayCode(s.getCode());
                    wayVo.setPayName(s.getName());
                    wayVos.add(wayVo);
                });
                modeVo.setPayWayVos(wayVos);
                payModes.add(modeVo);
            });
            deskVo.setQrCode(qrCodeUrl.toString());
            deskVo.setIsWallet(false);
            deskVo.setIsCredit(false);
            deskVo.setPayModes(payModes);
            deskVo.setTitle(receipt.getTitle());
            deskVo.setIsBreak(receipt.getIsBreak());
            deskVo.setOrderNo(receipt.getOrderNo());
            deskVo.setPayOrderNo(receipt.getPayOrderNo());
            deskVo.setPaymentAmount(receipt.getPayAmount());
            deskVo.setItemsAmount(receipt.getProductAmount());
            deskVo.setExtraAmount(receipt.getExtraAmount());
            deskVo.setOrderAmount(receipt.getFundAmount());
            deskVo.setReceiptAmount(receipt.getAmount());
            deskVo.setIsVoucherUrl(receipt.getIsVoucherUrl());
            deskVo.setIsVoucherNo(receipt.getIsVoucherNo());
            deskVo.setReceiptInfo(receiptInfo);
            deskVo.setReceiptCompanyMap(CopyObjectUtils.copyAlistToBlist(companyResps, CompanyInfoVO.class));
            //web收银台 && 收款公司开通了中金支付，做的特殊处理(暂时只返回这个支付方式：银行转账和聚合扫码支付和银行卡转账（快捷支付）)
//            if ((isPcDomain && receiptIsOpenCpcn) || (isPcDomain && tenantIsOpenCpcn)) {
//                ArrayList<String> payWayList = Lists.newArrayList(SupportCpcnPayWayEnums.BAR_CODE_SCAN.getCode(),
//                        SupportCpcnPayWayEnums.QUICK_BANK_CARD.getCode(), SupportCpcnPayWayEnums.OFFLINE_BANK_TRANSFER.getCode(),
//                        PayWayEnums.OFFLINE_TRANSFER.getCode());
//                SupportCpcnMch cpcnMch = supportCpcnMchManager.getByTenantId(tenantId);
//                if (ObjectUtils.isNotNull(cpcnMch)) {
//                    Integer receiptCompanyId = cpcnMch.getCompanyId();
//                    String[] companyIds = bankTransFilterCompanyIds.split(",");
//                    String[] quickFilterIds = quickBankCardFilterCompanyIds.split(",");
//                    if (Arrays.stream(companyIds).collect(Collectors.toList()).contains(receiptCompanyId.toString())) {
//                        payWayList.remove(SupportCpcnPayWayEnums.OFFLINE_BANK_TRANSFER.getCode());
//                    }
//                    if (Arrays.stream(quickFilterIds).collect(Collectors.toSet()).contains(receiptCompanyId.toString())) {
//                        payWayList.remove(SupportCpcnPayWayEnums.QUICK_BANK_CARD.getCode());
//                    }
//                }
//                payModes.forEach(r -> {
//                    List<PayWayVo> payWayVos = r.getPayWayVos();
//                    payWayVos = payWayVos.stream().filter(s -> payWayList.contains(s.getPayCode())).collect(Collectors.toList());
//                    r.setPayWayVos(payWayVos);
//                });
//                deskVo.setQrCode("");
//                deskVo.setPayModes(payModes);
//            }
            deskVo.setTenantId(tenantId);
            log.info("返回给前端的支付信息desk:{}", JSONObject.toJSONString(deskVo));
            return Result.build(deskVo);
        }

        QueryWanZhongOrdDetailResp data = null;
        ArrayList<Integer> orderTypes = Lists.newArrayList(OrderTypeEnums.SETTLEMENT_BATCH_ORDER.getCode(),
                OrderTypeEnums.CREDIT_REPAYMENT_ORDER.getCode());
        if (!orderTypes.contains(receipt.getOrderType())) {
            Result<QueryWanZhongOrdDetailResp> wzcResp = orderFeign.queryWanZhongOrdDetailByPayNo(param.getOrderNos().get(0));
            log.info("查询订单服务明确订单信息wzcResp:{}", JSONObject.toJSONString(wzcResp));
            if (!wzcResp.isSuccess() || ObjectUtils.isNull(wzcResp.getData())) {
                return Result.build(10000, "订单服务异常数据返回");
            }
            data = wzcResp.getData();
        }
        String payModule = PayDictCode.ALLINPAY_MODULE;
        String url = PayDictCode.ALLINPAY_NOTIFY_DNS;
        String rote = PayDictCode.ALLINPAY_QR_CODE_URl;
        String cmd = "086440075500001";
        String payNo = receipt.getPayOrderNo();
        if (receipt.getOrderType().equals(OrderTypeEnums.APP_STORE.getCode())) {
            cmd = "086440075500002";
            payModule = PayDictCode.QRCODE_MODULE;
            url = PayDictCode.QRCODE_URL;
            rote = PayDictCode.QRCODE_URL_ROTE;
            payNo = NumberSequence.getUnionPayNumber();
        }

        IPayAppInfo appInfo = getByCmd(cmd, tenantId);
        if (ObjectUtils.isEmpty(appInfo)) {
            return Result.build(100001, "无授权应用信息");
        }
        if (receipt.getOrderType().equals(OrderTypeEnums.APP_STORE.getCode())) {
            //支付请求
            PaymentPayLog payLog = new PaymentPayLog();
            log.info("业务单号:{}", receipt.getPayOrderNo());
            //请求信息
            payLog.setAppId(appInfo.getId());
            payLog.setDomain(DomainEnums.PC_DOMAIN.getCode());
            //业务单号
            payLog.setBusinessNo(receipt.getPayOrderNo());
            //支付中心请求流水
            payLog.setTradeNo(payNo);
            //支付发起时间
            payLog.setApplyTime(LocalDateTime.now());
            //创建时间
            payLog.setCreateTime(LocalDateTime.now());
            payLog.setNotifyService(NotifyServiceEnums.FINANCE.getCode());
            //快捷支付
            payLog.setPayMode(PayModeEnums.QUICK.getCode());
            payLog.setPayType(SupportPayTypeEnums.GZ.getCode());
            payLog.setPayAmount(receipt.getAmount().setScale(2, BigDecimal.ROUND_HALF_UP));
            //支付结果
            payLog.setPayResult(PayResultEnums.PAY_UNPAID.getCode());
            payLogManager.saveOrUpdate(payLog);
        }
        List<PayModeSetting> payModeSettings = modeSettingManager.list(new LambdaQueryWrapper<PayModeSetting>()
                .eq(PayModeSetting::getTenantId, tenantId)
        );


        Map<String, String> dictMap = dictService.dictMap(appInfo.getId().intValue(), receipt.getReceiptCompanyId(), payModule, null);
        if (dictMap.size() == 0) {
            dictMap = dictService.dictMap(appInfo.getId().intValue(), payModule, null);
        }
        String qrCodeUrl = dictMap.get(url) + dictMap.get(rote) + payNo + "&payUserId=" + RequestUserUtils.getUser().getId();
        log.info("查询出来的数据字典参数dictMap:{},通联聚合二维码地址qrCodeUrl:{}", JSONObject.toJSONString(dictMap), qrCodeUrl);
        boolean isWallet = false;
        BigDecimal availableBalance = account.entrySet().stream().map(Map.Entry::getValue)
                .map(AccountBalanceResp::getAvailableBalance).reduce(BigDecimal.ZERO, BigDecimal::add);
        if (receipt.getOrderType().equals(OrderTypeEnums.SETTLEMENT_BATCH_ORDER.getCode())) {
            availableBalance = balanceResp.getAvailableBalance();
        }
        if (availableBalance.compareTo(BigDecimal.ZERO) > 0) {
            isWallet = true;
            //收银台钱包可用余额
        }
        deskVo.setWalletAmount(availableBalance);
        //赊销配置
        boolean isCredit = false;
        deskVo.setCreditInfo(new CreditInfo());
        ArrayList<String> objects = Lists.newArrayList();
        //默认返回空对象
        if (companyId == 33 || receipt.getReceiptCompanyId() == 33) {
            CreditSettingResp credit = getCredit(companyId, receipt.getReceiptCompanyId());
            log.info("获取的赊销设置数据credit:{}", JSONObject.toJSONString(credit));
            if (param.getAmount().compareTo(receipt.getPayAmount()) == 0 && ObjectUtils.isNotNull(credit)
                    && !orderTypes.contains(receipt.getOrderType())) {
                BigDecimal multiply = credit.getCreditLimit().multiply(BigDecimal.valueOf(10000));
                if (multiply.compareTo(credit.getCreditUse()) > 0) {
                    isCredit = true;
                    BigDecimal creditLimit = credit.getCreditLimit().multiply(BigDecimal.valueOf(10000))
                            .setScale(2, BigDecimal.ROUND_HALF_UP);
                    BigDecimal breakLimit = credit.getBreakLimit().multiply(BigDecimal.valueOf(10000))
                            .setScale(2, BigDecimal.ROUND_HALF_UP);
                    CreditInfo creditInfo = new CreditInfo();
                    creditInfo.setCreditMode(1);
                    creditInfo.setFreeDays(credit.getFreeDays());
                    creditInfo.setBreakLimit(breakLimit.toPlainString());
                    creditInfo.setBreakRate(credit.getBreakRate().multiply(BigDecimal.valueOf(10000))
                            .setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());
                    creditInfo.setCreditLimit(creditLimit.toPlainString());
                    creditInfo.setCreditUse(credit.getCreditUse().setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());
                    creditInfo.setCycleDays(credit.getCycleDays());
                    creditInfo.setCycleInRate(credit.getCycleInRate().multiply(BigDecimal.valueOf(10000)).toPlainString());
                    creditInfo.setCycleOutRate(credit.getCycleOutRate().multiply(BigDecimal.valueOf(10000)).toPlainString());
                    creditInfo.setPayPercent(credit.getPayPercent().multiply(BigDecimal.valueOf(100))
                            .setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());
                    creditInfo.setEnableLimit(creditLimit.subtract(credit.getCreditUse())
                            .setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());
                    creditInfo.setMinPayLimit(credit.getPayPercent().multiply(receipt.getAmount())
                            .setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());
                    deskVo.setIsCredit(isCredit);
                    deskVo.setCreditInfo(creditInfo);
                    objects.add(PayWayEnums.CREDIT.getCode());
                }
            }
        } else {
            CreditConfigureResp creditConfigure = getCreditConfigure(receipt.getPayCompanyId(), receipt.getReceiptCompanyId());
            if (param.getAmount().compareTo(receipt.getPayAmount()) == 0 && ObjectUtils.isNotNull(creditConfigure)
                    && creditConfigure.getIsCredit() && creditConfigure.getLeaveCreditLimit()
                    .compareTo(receipt.getFundAmount()) >= 0 && !orderTypes.contains(receipt.getOrderType())) {
                isCredit = true;
                CreditInfo creditInfo = new CreditInfo();
                creditInfo.setCreditMode(0);
                creditInfo.setEnableLimit(creditConfigure.getLeaveCreditLimit().stripTrailingZeros().toPlainString());
                creditInfo.setInterestRate(creditConfigure.getInterestRate().stripTrailingZeros().toPlainString());
                creditInfo.setStartDate(creditConfigure.getStartDate());
                creditInfo.setEndDate(creditConfigure.getEndDate());
                creditInfo.setRepaymentDate(creditConfigure.getRepaymentDate());
                deskVo.setIsCredit(isCredit);
                deskVo.setCreditInfo(creditInfo);
                objects.add(PayWayEnums.CREDIT.getCode());
            } else {
                isCredit = false;
            }
        }
        if (receipt.getIsCredit()) {
            isCredit = false;
        }
        Map<String, String> payWayMap = dictService.dictMap(appInfo.getId().intValue(), receipt.getReceiptCompanyId(),
                PayDictCode.PAY_WAY_SPECIAL, null);
        if (payWayMap.isEmpty()) {
            payWayMap = dictService.dictMap(appInfo.getId().intValue(), PayDictCode.PAY_WAY_SPECIAL, null);
        }
        log.info("查询出配置特殊支付方式通道数据字典payWayMap:{}", payWayMap);
        String payWayStr = payWayMap.get(String.valueOf(receipt.getReceiptCompanyId()));

        if (StringUtils.isNotBlank(payWayStr)) {
            String[] split = payWayStr.split(",");
            Collections.addAll(objects, split);
        }
        if (ObjectUtils.isNotNull(data) && param.getDomain().equals(DomainEnums.APPLET_DOMAIN.getCode())) {
            List<AccountingMemberDTO> memberDTOS = subMchBindService.accountingMemberList(data.getSellerCompanyId(), tenantId);
            if (data.getIsWanZhongCityOrdFlag() && data.getIsHunPctPrepaymentsFlag() && CollectionUtils.isNotEmpty(memberDTOS)) {
                objects.add(PayWayEnums.WECHAT_PAY.getCode());
            } else if (!data.getIsWanZhongCityOrdFlag()) {
                MerchantSub sub = subManager.getOne(new LambdaQueryWrapper<MerchantSub>()
                        .eq(BaseModel::getTenantId, AppThreadLocal.getTenantId())
                );
                if (ObjectUtils.isNotNull(sub)) {
                    objects.add(PayWayEnums.WECHAT_PAY.getCode());
                }
                if (CollectionUtils.isNotEmpty(payModeSettings)) {
                    List<Long> modeIds = payModeSettings.stream().map(PayModeSetting::getModeId).distinct().collect(Collectors.toList());
                    List<PayMode> payModes = payModeManager.listByIds(modeIds);
                    List<String> payCodes = payModes.stream().map(PayMode::getModeValue).collect(Collectors.toList());
                    if (data.getIsHunPctPrepaymentsFlag() && payCodes.contains(PayWayEnums.CSC_HZPAY.getCode()) &&
                            receipt.getPayAmount().compareTo(LimitPayWayAmountEnums.CSC_HZPAY_LIMIT.getLimitAmount()) <= 0) {
                        HncMch mch = hncMchService.getHncMchByCompanyId(tenantId, receipt.getReceiptCompanyId().longValue());
                        if (ObjectUtils.isNotNull(mch) && mch.getOpenStatus() == 1) {
                            objects.add(PayWayEnums.CSC_HZPAY.getCode());
                            if (objects.contains(PayWayEnums.WECHAT_PAY.getCode())) {
                                objects.remove(PayWayEnums.WECHAT_PAY.getCode());
                            }
                        }
                    }
                }
            }
        }
        //支付方式
        List<PayMode> payWayList = payWayService.payWays(objects);
        log.info("支付方式:{}", JSONObject.toJSONString(payWayList));
        if (CollectionUtils.isEmpty(payWayList)) {
            return Result.build(PaymentResultCode.PaymentResultEnum.PAY_WAY_NOT_EXISTS.getCode(),
                    PaymentResultCode.PaymentResultEnum.PAY_WAY_NOT_EXISTS.getMessage());
        }
        Map<String, List<PayWayVo>> listMap = new HashMap<>(6);
        List<String> payCodes = new ArrayList<>();
        for (PayMode payMode : payWayList) {
            // 过滤app请求的支付方式
            if (DomainEnums.APP_DOMAIN.getCode().equals(param.getDomain())) {
                if (payMode.getModeCode().equals(PayWayEnums.ALLIN_PAY.getCode())) {
                    continue;
                }
            } else if (DomainEnums.PC_DOMAIN.getCode().equals(param.getDomain())) {
                if (payMode.getModeCode().equals(PayWayEnums.ALI_PAY.getCode()) ||
                        payMode.getModeCode().equals(PayWayEnums.WECHAT_PAY.getCode()) ||
                        payMode.getModeCode().equals(PayWayEnums.ALLINAPPLENT_PAY.getCode())
                        || payMode.getModeCode().equals(PayModeEnums.QUICK.getCode())) {
                    continue;
                }
            }
            if (receipt.getPayAmount().compareTo(BigDecimal.valueOf(50000)) > 0) {
                if (payMode.getModeCode().equals(PayWayEnums.ALLIN_PAY.getCode()) ||
                        payMode.getModeCode().equals(PayWayEnums.ALI_PAY.getCode()) ||
                        payMode.getModeCode().equals(PayWayEnums.WECHAT_PAY.getCode()) ||
                        payMode.getModeCode().equals(PayWayEnums.ALLINAPPLENT_PAY.getCode())) {
                    continue;
                }
            }
            if (CollectionUtils.isNotEmpty(receipt.getReceiptCompanyIds()) && receipt.getReceiptCompanyIds().size() > 1) {
                continue;
            }
            payCodes.add(payMode.getParentCode());
            PayWayVo payWayVo = new PayWayVo();
            payWayVo.setPayWay(payMode.getModeValue());
            payWayVo.setPayCode(payMode.getModeCode());
            payWayVo.setPayName(payMode.getModeName());
            if (listMap.get(payMode.getParentCode()) != null) {
                listMap.get(payMode.getParentCode()).add(payWayVo);
            } else {
                listMap.put(payMode.getParentCode(), Lists.newArrayList(payWayVo));
            }
        }
        if (receipt.getAmount().compareTo(receipt.getPayAmount()) != 0) {
            isCredit = false;
        }
        List<PayMode> payModes = payWayService.payModeList(payCodes);
        log.info("支付模式集合payModes:{}", JSONObject.toJSONString(payModes));
        List<PayModeVo> payModeVos = Lists.newArrayList();
        for (PayMode payMode : payModes) {
            //余额是否开通，未开通或者无余额不展示支付方式
            if (!isWallet && payMode.getModeCode().equals(PayModeEnums.WALLET.getCode())) {
                continue;
            }
            //不可用赊销
            if (!isCredit && payMode.getModeCode().equals(PayModeEnums.CREDIT.getCode())) {
                continue;
            }
            PayModeVo modeVo = CopyObjectUtils.copyAtoB(payMode, PayModeVo.class);
            payModeVos.add(modeVo);
        }
        payModeVos.stream().forEach(payModeVo -> {
            payModeVo.setPayWayVos(listMap.get(payModeVo.getModeCode()));
        });
        if (DomainEnums.PC_DOMAIN.getCode().equals(param.getDomain()) &&
                receipt.getOrderType().equals(OrderTypeEnums.APP_STORE.getCode())) {
            deskVo.setQrCode(qrCodeUrl);
        }
        deskVo.setIsWallet(isWallet);
        deskVo.setIsCredit(isCredit);
        deskVo.setPayModes(payModeVos);
        deskVo.setReceiptInfo(receiptInfo);
        deskVo.setTitle(receipt.getTitle());
        deskVo.setIsBreak(receipt.getIsBreak());
        deskVo.setOrderNo(receipt.getOrderNo());
        deskVo.setPayOrderNo(receipt.getPayOrderNo());
        deskVo.setPaymentAmount(receipt.getPayAmount());
        deskVo.setItemsAmount(receipt.getProductAmount());
        deskVo.setExtraAmount(receipt.getExtraAmount());
        deskVo.setOrderAmount(receipt.getFundAmount());
        deskVo.setReceiptAmount(receipt.getAmount());
        deskVo.setIsVoucherUrl(receipt.getIsVoucherUrl());
        deskVo.setIsVoucherNo(receipt.getIsVoucherNo());
        log.info("返回给前端的支付信息desk:{}", JSONObject.toJSONString(deskVo));
        return Result.build(deskVo);
    }

    /**
     * web收银台，中金支付的特殊处理(易梯和悦安居-中金支付通道的web收银台是华华的站点，这里转换成中金支付平台商户的站点)
     *
     * @param receiptCompanyId
     * @param currTenantId
     * @return
     */
    public ReceiptTenantReturnDTO cpcnPayOfTenantConvert(Integer receiptCompanyId, String currTenantId) {
        //只有开通了中金支付的才能查到中金支付平台商户信息，否则直接返回原来的站点id
        ReceiptTenantReturnDTO receiptTenantIdDTO = new ReceiptTenantReturnDTO();
        receiptTenantIdDTO.setReceiptIsOpenCpcn(false);
        receiptTenantIdDTO.setTenantIsOpenCpcn(false);
        receiptTenantIdDTO.setCurrTenantId(currTenantId);
        List<SupportCpcnMch> receiptMchList = supportCpcnMchManager.getByCompanyId(receiptCompanyId);
        if (CollectionUtils.isNotEmpty(receiptMchList)) {
            //暂时一个公司只会配置一个站点，如果后面一个公司配置多站点需要再处理
            currTenantId = receiptMchList.get(0).getTenantId();
            receiptTenantIdDTO.setReceiptIsOpenCpcn(true);
            receiptTenantIdDTO.setCurrTenantId(currTenantId);
        }
        List<SupportCpcnMch> tenantMchList = supportCpcnMchManager.getListByTenantId(currTenantId);
        if (CollectionUtils.isNotEmpty(tenantMchList)) {
            receiptTenantIdDTO.setTenantIsOpenCpcn(true);
        }
        return receiptTenantIdDTO;
    }


    private Map<ChannelRouteInterfaceTypeEnums, IPayUnifiedPayStrategy> unifiedPayStrategyMap = new HashMap<>();

    @Resource
    private void setUnifiedPayStrategyMap(List<IPayUnifiedPayStrategy> onlineStrategyList) {
        onlineStrategyList.stream().forEach(r -> {
            PayChannelInterfaceHandler annotation = AnnotationUtils.findAnnotation(r.getClass(), PayChannelInterfaceHandler.class);
            if (ObjectUtils.isNotNull(annotation)) {
                unifiedPayStrategyMap.put(annotation.value(), r);
            }
        });
    }

    /**
     * 统一支付调度
     *
     * @param param
     * @return
     * @throws AlipayApiException
     */
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public Result<OnlinePayParametersVo> unifiedPay(HttpServletRequest request, UnifiedPayParam param) {
        log.info("统一支付方式起调param：{}", JSONObject.toJSONString(param));
        String key = lock_pay_commit_apply + param.getPayOrderNo();
        if (!redisLock.getLock(key, notifyOutTime, TimeUnit.SECONDS)) {
            log.info("收银台支付请求重复执行:{}", lock_pay_commit_apply);
            throw new BusinessException(50000, "支付处理中，请稍后！");
        }
        String tenantId = StringUtils.isBlank(param.getTenantId()) ? AppThreadLocal.getTenantId() : param.getTenantId();
        Integer companyId = RequestUserUtils.getUser().getCompanyId();
        ReceiptOnlineReq req = CopyObjectUtils.copyAtoB(param, ReceiptOnlineReq.class);
        req.setPayCompanyId(companyId);
        ReceiptOnlineResp resp = queryReceiptOnline(req);
        log.info("收款流水记录resp:{}", JSONObject.toJSONString(resp));
        if (ObjectUtils.isEmpty(resp)) {
            return Result.build(PaymentResultCode.PaymentResultEnum.RECEIPT_NOT_EXISTS.getCode(),
                    PaymentResultCode.PaymentResultEnum.RECEIPT_NOT_EXISTS.getMessage());
        }
        String ip = IpUtil.api.getIpAddress(request);
        param.setClientIp(ip);

        //web收银台，中金支付的特殊处理(易梯和悦安居-中金支付通道的web收银台是华华的站点，这里转换成中金支付平台商户的站点)
        if (DomainEnums.PC_DOMAIN.getCode().equals(param.getDomain())) {
            ReceiptTenantReturnDTO returnDTO = cpcnPayOfTenantConvert(resp.getReceiptCompanyId(), tenantId);
            tenantId = returnDTO.getCurrTenantId();
        }

        List<SupportPaymentPayWayDTO> payWayListByRoute = paymentPayWayManager.getPayWayListByRoute(tenantId);
        if (CollectionUtils.isNotEmpty(payWayListByRoute)) {
            SupportPaymentPayWayDTO payWayDTO = payWayListByRoute.stream().filter(r -> r.getCode()
                    .equals(param.getPayWay()) && r.getChannelCode().equals(param.getPayMode())).findFirst().orElse(null);
            if (ObjectUtils.isNull(payWayDTO)) {
                throw new BusinessException(80071, "商户未开通相关支付方式或支付渠道！");
            }
            ChannelRouteInterfaceTypeEnums interfaceEnum = ChannelRouteInterfaceTypeEnums.getByInterfaceCode(payWayDTO.getInterfaceCode());
            IPayUnifiedPayStrategy unifiedPayStrategy = unifiedPayStrategyMap.get(interfaceEnum);
            SupportUnifiedPayDTO dto = CopyObjectUtils.copyAtoB(param, SupportUnifiedPayDTO.class);
            dto.setTenantId(tenantId);
            dto.setChannelId(payWayDTO.getChannelId());
            dto.setPayMode(payWayDTO.getChannelCode());
            OnlinePayParametersVo vo = unifiedPayStrategy.unifiedPre(dto, resp);
            return Result.build(vo);
        }
        PaymentResultCode.PaymentResultEnum resultEnum = checkParam(param, resp.getIsVoucherNo(), resp.getIsVoucherUrl());
        if (ObjectUtils.isNotEmpty(resultEnum)) {
            return Result.build(resultEnum.getCode(), resultEnum.getMessage());
        }
        param.setTenantId(tenantId);
        BigDecimal payAmount = ObjectUtils.isNotNull(param.getPayAmount()) ? param.getPayAmount() : BigDecimal.ZERO;
        BigDecimal walletAmount = ObjectUtils.isNotNull(param.getWalletAmount()) ? param.getWalletAmount() : BigDecimal.ZERO;
        BigDecimal creditAmount = ObjectUtils.isNotNull(param.getCreditAmount()) ? param.getCreditAmount() : BigDecimal.ZERO;
        if (payAmount.add(walletAmount).add(creditAmount).compareTo(resp.getAmount()) > 0) {
            throw new BusinessException(50000, "支付金额超出款项应付金额，确认后重试！");
        }
        if (!param.getPayMode().equals(PayModeEnums.QUICK.getCode())) {
            log.info("统一支付线下支付");
            return unifiedOfflinePay(param, resp);
        }
        QueryWanZhongOrdDetailResp data = null;
        ArrayList<Integer> orderTypes = Lists.newArrayList(OrderTypeEnums.SETTLEMENT_BATCH_ORDER.getCode(),
                OrderTypeEnums.CREDIT_REPAYMENT_ORDER.getCode());
        if (!orderTypes.contains(resp.getOrderType())) {
            Result<QueryWanZhongOrdDetailResp> wzcResp = orderFeign.queryWanZhongOrdDetailByPayNo(resp.getPayNo());
            log.info("查询订单服务明确订单信息wzcResp:{}", JSONObject.toJSONString(wzcResp));
            if (!wzcResp.isSuccess() || ObjectUtils.isNull(wzcResp.getData())) {
                return Result.build(10000, "订单服务异常数据返回");
            }
            data = wzcResp.getData();
            param.setIsFunPercent(data.getIsHunPctPrepaymentsFlag());
        }
        CcbPayParam payParam = new CcbPayParam();
        payParam.setPayWay(param.getPayWay());
        payParam.setCode(param.getCode());
        payParam.setBusinessNo(resp.getPayOrderNo());
        payParam.setCompanyId(resp.getReceiptCompanyId());
        payParam.setOrderAmount(resp.getAmount());
        payParam.setOrderName(resp.getTitle());
        payParam.setTenantId(tenantId);
        payParam.setTransAmount(param.getPayAmount());
        payParam.setPayNo(resp.getPayNo());
        String cmd = "086440075500001";
        if (resp.getOrderType().equals(OrderTypeEnums.APP_STORE.getCode())) {
            cmd = "086440075500002";
        }
        IPayAppInfo appInfo = getByCmd(cmd, tenantId);
        if (ObjectUtils.isEmpty(appInfo)) {
            return Result.build(100001, "无授权应用信息");
        }
        if (param.getDomain().equals(DomainEnums.APPLET_DOMAIN.getCode()) &&
                resp.getOrderType().equals(OrderTypeEnums.SHOP_STORE.getCode()) &&
                param.getPayWay().equals(PayWayEnums.WECHAT_PAY.getCode())) {
            if (!data.getIsWanZhongCityOrdFlag()) {
                return apiV3Pay(request, param, resp);
            } else if (data.getIsWanZhongCityOrdFlag() && data.getIsHunPctPrepaymentsFlag()) {
                List<AccountingMemberDTO> memberDTOS = subMchBindService.accountingMemberList(data.getSellerCompanyId(),
                        StringUtils.isBlank(param.getTenantId()) ? AppThreadLocal.getTenantId() : param.getTenantId());
                if (CollectionUtils.isEmpty(memberDTOS)) {
                    return Result.build(100001, "商家无分账信息，请联系平台处理");
                }
                payParam.setPayCmd(CcbPayCmdEnums.APPLET.getCode());
                payParam.setMemberList(memberDTOS);
                OnlinePayParametersVo vo = ccbPayService.unifiedPay(payParam, appInfo.getId(), DomainEnums.APPLET_DOMAIN, PayModeEnums.QUICK);
                return Result.build(vo);
            }
        }
        AppHandleTypeEnums handleTypeEnums = AppHandleTypeEnums.getByCode(appInfo.getHandleType());
        switch (handleTypeEnums) {
            case PLAT:
                if (param.getPayMode().equals(PayModeEnums.QUICK.getCode())) {
                    log.info("统一支付快捷支付");
                    return unifiedOnlinePay(request, param, resp, appInfo.getId());
                }
                break;
            case WZC_HSB:
                payParam.setPayCmd(CcbPayCmdEnums.PC.getCode());
                OnlinePayParametersVo vo = ccbPayService.unifiedPay(payParam, appInfo.getId(), DomainEnums.PC_DOMAIN, PayModeEnums.QUICK);
                return Result.build(vo);
        }
        return null;
    }


    /**
     * 统一支付(online)
     *
     * @param param
     * @return
     */
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public Result<OnlinePayParametersVo> unifiedOnlinePay(HttpServletRequest request, UnifiedPayParam param,
                                                          ReceiptOnlineResp receiptOrder, Long appId) {
        log.info("统一支付快捷支付参数 param:{}, receiptOrder:{}", JSONObject.toJSONString(param), JSONObject.toJSONString(receiptOrder));
        try {
            OnlinePayParametersVo parametersVo = new OnlinePayParametersVo();
            parametersVo.setPayResult(PayResultEnums.PAY_UNPAID.getCode());
            Integer notifyService = NotifyServiceEnums.FINANCE.getCode();
            if (receiptOrder.getOrderType().equals(OrderTypeEnums.APP_STORE.getCode())) {
                notifyService = NotifyServiceEnums.FINANCE.getCode();
            }
            String ip = IpUtil.api.getIpAddress(request);
            if (param.getPayWay().equals(PayWayEnums.ALI_PAY.getCode())) {
                //支付宝
                AliPayAppParam aliPayAppParam = new AliPayAppParam();
                aliPayAppParam.setAmount(receiptOrder.getPayAmount().setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());
                aliPayAppParam.setBusinessNo(receiptOrder.getPayOrderNo());
                aliPayAppParam.setBody(receiptOrder.getTitle());
                aliPayAppParam.setSubject(receiptOrder.getTitle());
                aliPayAppParam.setPayCompanyId(receiptOrder.getPayCompanyId());
                aliPayAppParam.setReceiptCompanyId(receiptOrder.getReceiptCompanyId());
                String params = aliPayService.alipayForApp(aliPayAppParam, appId, notifyService);
                parametersVo.setParams(params);
                parametersVo.setPayChannel(PayWayEnums.ALI_PAY.toString());
            } else if (param.getDomain().equals(DomainEnums.APPLET_DOMAIN.getCode()) &&
                    !param.getPayWay().equals(PayWayEnums.CSC_HZPAY.getCode())) {
                //微信小程序
                WeChatPayParam wechatPayParam = new WeChatPayParam();
                wechatPayParam.setIp(ip);
                wechatPayParam.setProductName(receiptOrder.getTitle());
                wechatPayParam.setBusinessNo(receiptOrder.getPayOrderNo());
                wechatPayParam.setPrice(receiptOrder.getPayAmount());
                wechatPayParam.setCode(param.getCode());
                wechatPayParam.setPayCompanyId(receiptOrder.getPayCompanyId());
                wechatPayParam.setReceiptCompanyId(receiptOrder.getReceiptCompanyId());
                parametersVo.setParams(wechatPayService.wechatAppletPay(wechatPayParam, appId, notifyService));
                parametersVo.setPayChannel(PayWayEnums.WECHAT_PAY.toString());
            } else if (param.getPayWay().equals(PayWayEnums.WECHAT_PAY.getCode())) {
                //微信
                WeChatPayParam wechatPayParam = new WeChatPayParam();
                wechatPayParam.setProductName(receiptOrder.getTitle());
                wechatPayParam.setBusinessNo(receiptOrder.getPayOrderNo());
                wechatPayParam.setPrice(receiptOrder.getPayAmount());
                wechatPayParam.setIp(ip);
                wechatPayParam.setCompanyId(receiptOrder.getReceiptCompanyId());
                wechatPayParam.setPayCompanyId(receiptOrder.getPayCompanyId());
                wechatPayParam.setReceiptCompanyId(receiptOrder.getReceiptCompanyId());
                parametersVo.setParams(wechatPayService.wechatPay(wechatPayParam, appId, notifyService));
                parametersVo.setPayChannel(PayWayEnums.WECHAT_PAY.toString());
            } else if (param.getPayWay().equals(PayWayEnums.CSC_HZPAY.getCode()) && param.getIsFunPercent()) {
                HncMch mch = hncMchService.getHncMchByCompanyId(param.getTenantId(), receiptOrder.getReceiptCompanyId().longValue());
                if (ObjectUtils.isNull(mch) || mch.getOpenStatus() != 1) {
                    throw new BusinessException(52220, "华正支付商户未开通，请切换支付方式！");
                }
                if (receiptOrder.getAmount().compareTo(LimitPayWayAmountEnums.CSC_HZPAY_LIMIT.getLimitAmount()) > 0) {
                    throw new BusinessException(52220, "华正支付大于限额，请确认后重试！");
                }
                Result<List<String>> listResult = orderFeign.getCustomizeCatByPayNo(receiptOrder.getPayNo());
                if (listResult.getCode() != 0) {
                    throw new BusinessException(52221, "查询订单信息异常，请确认后重试！");
                }
                String title = receiptOrder.getTitle();
                List<String> data = listResult.getData();
                if (CollectionUtils.isNotEmpty(data)) {
                    title = title + ";" + data.stream().collect(Collectors.joining(";"));
                }
                HzPayParam hzPayParam = new HzPayParam();
                hzPayParam.setCode(param.getCode());
                hzPayParam.setProductName(title);
                hzPayParam.setBusinessNo(receiptOrder.getPayOrderNo());
                hzPayParam.setPrice(receiptOrder.getPayAmount());
                hzPayParam.setIp(ip);
                hzPayParam.setTenantId(param.getTenantId());
                hzPayParam.setCompanyId(receiptOrder.getReceiptCompanyId());
                hzPayParam.setPayCompanyId(receiptOrder.getPayCompanyId());
                hzPayParam.setReceiptCompanyId(receiptOrder.getReceiptCompanyId());
                parametersVo.setParams(hzUnifiedPayService.hzPayApplet(hzPayParam, appId, notifyService));
                parametersVo.setPayChannel(PayWayEnums.CSC_HZPAY.getCode());
            }
            return Result.build(parametersVo);
        } catch (BaseException e) {
            log.error("快捷支付异常e：{}", e.getMessage());
            return Result.build(e.getCode(), e.getMessage());
        }
    }

    /**
     * 统一支付线下(offline)
     * 包含组合
     *
     * @param param
     * @return
     */
    @Transactional(readOnly = false, rollbackFor = BaseException.class)
    public Result<OnlinePayParametersVo> unifiedOfflinePay(UnifiedPayParam param, ReceiptOnlineResp receiptOrder) {
        try {
            Integer companyId = RequestUserUtils.getUser().getCompanyId();
            log.info("统一支付线下支付参数param:{},receiptOrder:{},当前公司id:{}",
                    JSONObject.toJSONString(param), JSONObject.toJSONString(receiptOrder), companyId);
            OnlinePayParametersVo parametersVo = new OnlinePayParametersVo();
            parametersVo.setPayChannel(param.getPayWay());
            parametersVo.setPayResult(PayResultEnums.PAY_SUCCESS.getCode());
            //统一收款日志内容
            //收款支付流水号
            //使用余额支付
            Map<Integer, AccountBalanceResp> accountMap = getAccountBalance(companyId, receiptOrder.getReceiptCompanyId(),
                    receiptOrder.getSourceCompanyId(), receiptOrder.getToCompanyId());
            log.info("账户信息accountMap:{}", JSONObject.toJSONString(accountMap));
            if (ObjectUtils.isEmpty(accountMap)) {
                return Result.build(PaymentResultCode.PaymentResultEnum.ACCOUNT_NOT_EXISTS.getCode(),
                        PaymentResultCode.PaymentResultEnum.ACCOUNT_NOT_EXISTS.getMessage());
            }
            String payMode = param.getPayMode();
            String payWay = param.getPayWay();
            //平台账户信息
            AccountBalanceResp unionAccount = accountMap.get(AccountTypeEnums.NORMALLY_ACCOUNT.getCode());
            //定向账户信息
            AccountBalanceResp dirtAccount = accountMap.get(AccountTypeEnums.SPECIALLY_ACCOUNT.getCode());
            BigDecimal dirtAmount = null, unAmount = null;
            BigDecimal balanceAmount = null;
            Long dirtAccountId = null, unAccountId = unionAccount.getId();
            Integer creditMode = null;
            if (param.getPayWay().equals(PayWayEnums.WALLET.getCode())) {
                //使用余额支付
                BigDecimal walletAmount = param.getWalletAmount();
                BigDecimal unBalance = unionAccount.getAvailableBalance();
                if (ObjectUtils.isNotNull(dirtAccount) && !receiptOrder.getOrderType()
                        .equals(OrderTypeEnums.SETTLEMENT_BATCH_ORDER.getCode())) {
                    //定向账户信息
                    BigDecimal dirtBalance = dirtAccount.getAvailableBalance();
                    dirtAccountId = dirtAccount.getId();
                    if (dirtBalance.compareTo(walletAmount) >= 0) {
                        dirtAmount = walletAmount;
                        balanceAmount = walletAmount;
                    } else if (dirtBalance.add(unBalance).compareTo(walletAmount) >= 0) {
                        dirtAmount = dirtBalance;
                        unAmount = walletAmount.subtract(dirtAmount);
                        balanceAmount = walletAmount;
                    } else {
                        if (CollectionUtils.isEmpty(param.getVoucherUrls())) {
                            throw new BusinessException(PaymentResultCode.PaymentResultEnum.WALLET_CANT_SHORTAGE.getCode()
                                    , PaymentResultCode.PaymentResultEnum.WALLET_CANT_SHORTAGE.getMessage());
                        }
                        dirtAmount = dirtBalance;
                        unAmount = unBalance;
                        balanceAmount = dirtBalance.add(unBalance);
                    }
                } else {
                    //平台账户
                    if (unBalance.compareTo(walletAmount) >= 0) {
                        unAmount = walletAmount;
                        balanceAmount = walletAmount;
                    } else {
                        if (ObjectUtils.isNull(param.getVoucherUrls()) || CollectionUtils.isEmpty(param.getVoucherUrls())) {
                            throw new BusinessException(PaymentResultCode.PaymentResultEnum.WALLET_CANT_SHORTAGE.getCode()
                                    , PaymentResultCode.PaymentResultEnum.WALLET_CANT_SHORTAGE.getMessage());
                        }
                        unAmount = unBalance;
                        balanceAmount = unBalance;
                    }
                }
                if (ObjectUtils.isNotNull(param.getPayAmount()) && param.getPayAmount().compareTo(BigDecimal.ZERO) > 0) {
                    payMode = PayModeEnums.GROUP.getCode();
                    payWay = PayWayEnums.GROUP.getCode();
                }
            } else if (param.getPayWay().equals(PayWayEnums.CREDIT.getCode())) {
                //使用赊销
                if (receiptOrder.getIsCredit() != null && receiptOrder.getIsCredit()) {
                    return Result.build(PaymentResultCode.PaymentResultEnum.CREDIT_CANT_AGAIN.getCode(),
                            PaymentResultCode.PaymentResultEnum.CREDIT_CANT_AGAIN.getMessage());
                }
                if (receiptOrder.getReceiptCompanyId() == 33 || receiptOrder.getPayCompanyId() == 33) {
                    creditMode = 1;
                    BigDecimal payAmount = param.getPayAmount() == null ? BigDecimal.ZERO : param.getPayAmount();
                    BigDecimal walletAmount = param.getWalletAmount() == null ? BigDecimal.ZERO : param.getWalletAmount();
                    PaymentResultCode.PaymentResultEnum resultEnum = checkCredit(companyId, receiptOrder.getReceiptCompanyId(),
                            receiptOrder.getPayAmount(), payAmount.add(walletAmount), param.getCreditAmount());
                    if (ObjectUtils.isNotEmpty(resultEnum)) {
                        return Result.build(resultEnum.getCode(), resultEnum.getMessage());
                    }
                    if (param.getWalletAmount().compareTo(BigDecimal.ZERO) > 0) {
                        //使用余额支付
                        walletAmount = param.getWalletAmount();
                        BigDecimal unBalance = unionAccount.getAvailableBalance();
                        if (!ObjectUtils.isEmpty(dirtAccount)) {
                            //定向账户信息
                            BigDecimal dirtBalance = dirtAccount.getAvailableBalance();
                            dirtAccountId = dirtAccount.getId();
                            if (dirtBalance.compareTo(walletAmount) >= 0) {
                                dirtAmount = walletAmount;
                            } else if (dirtBalance.add(unBalance).compareTo(walletAmount) >= 0) {
                                dirtAmount = dirtBalance;
                                unAmount = walletAmount.subtract(dirtAmount);
                            } else {
                                dirtAmount = dirtBalance;
                                unAmount = unBalance;
                            }
                        } else {
                            //平台账户
                            if (unBalance.compareTo(walletAmount) >= 0) {
                                unAmount = walletAmount;
                            } else {
                                unAmount = unBalance;
                            }
                        }
                        BigDecimal add = dirtAmount != null ? dirtAmount.add(unAmount) : unAmount;
                        if (add.compareTo(walletAmount) != 0) {
                            throw new BusinessException(PaymentResultCode.PaymentResultEnum.WALLET_CANT_SHORTAGE.getCode()
                                    , PaymentResultCode.PaymentResultEnum.WALLET_CANT_SHORTAGE.getMessage());
                        }
                    }
                } else {
                    QueryCreditReq req = new QueryCreditReq();
                    req.setCompanyId(receiptOrder.getReceiptCompanyId());
                    req.setCreditCompanyId(receiptOrder.getPayCompanyId());
                    Result<CreditConfigureResp> configure = creditConfigureFeignClient.getCreditConfigureByCompanyId(req);
                    if (!configure.isSuccess()) {
                        throw new BusinessException(50002, "赊销已停用，暂无法使用赊销支付！");
                    }
                    CreditConfigureResp configureResp = configure.getData();
                    if (!configureResp.getIsCredit()) {
                        throw new BusinessException(50002, "赊销已停用，暂无法使用赊销支付！");
                    }
                    BigDecimal leaveCreditLimit = configureResp.getLeaveCreditLimit();
                    if (receiptOrder.getAmount().compareTo(leaveCreditLimit) > 0) {
                        throw new BusinessException(50002, "赊销金额超出剩余限额");
                    }
                    creditMode = 0;
                }
            }
            String tradeNo = NumberSequence.getUnionPayNumber();
            ReceiptLog receiptLog = CopyObjectUtils.copyAtoB(param, ReceiptLog.class);
            if (CollectionUtils.isNotEmpty(param.getVoucherUrls())) {
                receiptLog.setVoucherUrl(JSONObject.toJSONString(param.getVoucherUrls()));
            }
            receiptLog.setTradeNo(tradeNo);
            receiptLog.setPayTime(LocalDateTime.now());
            receiptLog.setConfirmTime(LocalDateTime.now());
            receiptLog.setOrderNo(receiptOrder.getOrderNo());
            receiptLog.setUserId(RequestUserUtils.getUser().getMemberId());
            receiptLog.setBusinessNo(receiptOrder.getPayOrderNo());
            receiptLog.setPayResult(PayResultEnums.PAY_SUCCESS.getCode());
            receiptLog.setBusinessType(BusinessTypeEnums.RECEIPT.getCode());
            receiptLogManager.saveOrUpdate(receiptLog);
            ReceiptOrderDetailReq detailReq = new ReceiptOrderDetailReq();
            detailReq.setPayWay(payWay);
            detailReq.setPayMode(payMode);
            detailReq.setPayTradeNo(tradeNo);
            detailReq.setPlatformAmount(unAmount);
            detailReq.setDirectAmount(dirtAmount);
            detailReq.setDirectAccountId(dirtAccountId);
            detailReq.setPlatformAccountId(unAccountId);
            detailReq.setPayAmount(param.getPayAmount());
            detailReq.setVoucherNo(param.getVoucherNo());
            detailReq.setCreditDays(param.getCreditDays());
            detailReq.setCreditAmount(param.getCreditAmount());
            detailReq.setPayOrderNo(receiptOrder.getPayOrderNo());
            detailReq.setBalancePayAmount(param.getWalletAmount());
            detailReq.setMemberId(RequestUserUtils.getUser().getMemberId());
            detailReq.setVoucherUrl(CollectionUtils.isNotEmpty(param.getVoucherUrls()) ? param.getVoucherUrls()
                    .toArray(new String[param.getVoucherUrls().size()]) : null);
            detailReq.setPayUserId(RequestUserUtils.getUser().getId());
            detailReq.setCreditMode(creditMode);
            detailReq.setRemark(param.getRemark());
            log.info("回调收款信息给财务detail:{}", JSONObject.toJSONString(detailReq));
            Result result = receiptOrderFeignClient.offlineUpdateReceiptOrderDetail(detailReq);
            if (result.getCode() != 0) {
                throw new BusinessException(result.getCode(), result.getMessage());
            }
            return Result.build(parametersVo);
        } catch (BusinessException e) {
            log.error("支付中心收银台异常e：{}", e.getMessage());
            return Result.build(e.getCode(), e.getMessage());
        } catch (Exception e) {
            log.error("支付中心收银台异常e：{}", e);
            return Result.build(PaymentResultCode.PaymentResultEnum.PAYMENT_PAY_EXCEPTION.getCode(),
                    PaymentResultCode.PaymentResultEnum.PAYMENT_PAY_EXCEPTION.getMessage());
        }
    }


    /**
     * 统一支付（聚合二维码支付）
     *
     * @param request
     * @param response
     * @throws IOException
     */
    @Transactional(readOnly = false)
    public void unifiedQrPay(HttpServletRequest request, HttpServletResponse response, String payOrderNo,
                             Integer payUserId) throws IOException {
        log.info("统一支付聚合二维码支付参数payOrderNo:{}", payOrderNo);
        String userAgent = request.getHeader("user-agent");
        ReceiptOnlineReq req = new ReceiptOnlineReq();
        req.setPayOrderNo(payOrderNo);
        ReceiptOnlineResp receiptOrderResp = queryReceiptOnline(req);
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        log.info("收款记录流水receiptOrderResp:{}", JSONObject.toJSONString(receiptOrderResp));
        if (ObjectUtils.isEmpty(receiptOrderResp)) {
            response.getWriter().write("未查到付款信息");
            return;
        }
        BigDecimal dealMoney = receiptOrderResp.getPayAmount();
        BigDecimal rote = BigDecimal.valueOf(100);
        BigDecimal multiply = dealMoney.multiply(rote);
        AllinPayParam param = new AllinPayParam();
        param.setDealMoney(multiply.longValue());
        param.setOrderName(receiptOrderResp.getTitle());
        param.setBusinessNo(receiptOrderResp.getPayOrderNo());
        param.setPayUserId(payUserId);
        String codeUrl = "";
        Map<String, String> stringStringMap = null;
        if (userAgent != null && userAgent.contains("MicroMessenger")) {
            param.setDealMethod("W01");
            param.setPayWay(PayWayEnums.WECHAT_PAY.getCode());
            stringStringMap = allinPayService.unifiedPay(param);
            if (stringStringMap.get("payinfo") != null) {
                codeUrl = stringStringMap.get("payinfo");
            } else {
                throw new BusinessException(30000, stringStringMap.get("errmsg"));
            }
        } else if (userAgent != null && userAgent.contains("AlipayClient")) {
            param.setDealMethod("A01");
            param.setPayWay(PayWayEnums.ALI_PAY.getCode());
            stringStringMap = allinPayService.unifiedPay(param);
            if (stringStringMap.get("payinfo") != null) {
                codeUrl = stringStringMap.get("payinfo");
            } else {
                throw new BusinessException(30000, stringStringMap.get("errmsg"));
            }
        }
        log.info("统一支付聚合二维码支付codeUrl:{}", codeUrl);
        response.sendRedirect(codeUrl);
    }


    /**
     * 支付结果查询
     */
    public Result getPayStatus(String payOrderNo) {
        log.info("订单支付结果查询 流水号payOrderNo:{}", payOrderNo);
        if (StringUtils.isNotBlank(payOrderNo)) {
            PaymentPayLog paymentPayLog = payLogManager.getOne(new QueryWrapper<PaymentPayLog>().lambda()
                    .eq(PaymentPayLog::getBusinessNo, payOrderNo)
                    .eq(PaymentPayLog::getPayResult, PayResultEnums.PAY_SUCCESS.getCode()));
            if (paymentPayLog != null) {
                return Result.build(PaymentStatusEnums.PAYMENT_SUCCESS.getDesc());
            } else {
                return Result.build(PaymentStatusEnums.PAYMENT_UNPAID.getCode(), PaymentStatusEnums.PAYMENT_UNPAID.getDesc());
            }
        } else {
            return Result.build(PaymentStatusEnums.PAYMENT_UNPAID.getCode(), PaymentStatusEnums.PAYMENT_UNPAID.getDesc());
        }
    }


    /**
     * 数据检查
     *
     * @param param
     * @return
     */
    private PaymentResultCode.PaymentResultEnum checkParam(UnifiedPayParam param, Boolean isVoucherNo, Boolean isVoucherUrl) {
        DomainEnums domainEnums = DomainEnums.getByCode(param.getDomain());
        //请求域检查
        if (ObjectUtils.isEmpty(domainEnums)) {
            return PaymentResultCode.PaymentResultEnum.DOMAIN_NOT_EXISTS;
        }
        //支付模式检查
        PayModeEnums modeEnums = PayModeEnums.getByCode(param.getPayMode());
        if (ObjectUtils.isEmpty(modeEnums)) {
            return PaymentResultCode.PaymentResultEnum.PAY_MODE_NOT_EXISTS;
        }
        //支付方式检查
        PayWayEnums enums = PayWayEnums.getByCode(param.getPayWay());
        if (ObjectUtils.isEmpty(enums)) {
            return PaymentResultCode.PaymentResultEnum.PAY_WAY_NOT_EXISTS;
        }
        if (StringUtils.isEmpty(param.getPayOrderNo())) {
            return PaymentResultCode.PaymentResultEnum.PAY_ORDER_NO_NOT_EXISTS;
        }
        //组合检查 pc聚合支付的只有扫码但是不在此方法
        if (modeEnums.getCode().equals(PayModeEnums.QUICK.getCode()) &&
                domainEnums.getCode().equals(DomainEnums.PC_DOMAIN.getCode())) {
            return PaymentResultCode.PaymentResultEnum.PAY_WAY_NOT_EXISTS;
        }
        BigDecimal amount = param.getAmount();
        BigDecimal walletAmount = param.getWalletAmount() == null ? BigDecimal.ZERO : param.getWalletAmount();
        BigDecimal payAmount = param.getPayAmount() == null ? BigDecimal.ZERO : param.getPayAmount();
        BigDecimal creditAmount = param.getCreditAmount() == null ? BigDecimal.ZERO : param.getCreditAmount();
        BigDecimal add = walletAmount.add(payAmount).add(creditAmount);
        if (add.compareTo(BigDecimal.valueOf(0.01)) < 0) {
            return PaymentResultCode.PaymentResultEnum.AMOUNT_LASS_EXCEPTION;
        }
        if (add.compareTo(amount) != 0) {
            return PaymentResultCode.PaymentResultEnum.AMOUNT_HAVE_EXCEPTION;
        }
        if (enums.getCode().equals(PayWayEnums.WALLET.getCode())) {
            //余额支付检查
            if (walletAmount.compareTo(BigDecimal.ZERO) == 0) {
                return PaymentResultCode.PaymentResultEnum.WALLET_NOT_ZERO;
            }
            if (walletAmount.compareTo(amount) > 0) {
                return PaymentResultCode.PaymentResultEnum.WALLET_GT_AMOUNT;
            }
            if (amount.compareTo(walletAmount) > 0) {
                PaymentResultCode.PaymentResultEnum resultEnum = checkVoucher(payAmount, param.getVoucherNo(),
                        param.getVoucherUrls(), isVoucherNo, isVoucherUrl);
                return resultEnum;
            }
            BigDecimal groupAmount = walletAmount.add(payAmount);
            if (groupAmount.compareTo(amount) != 0) {
                return PaymentResultCode.PaymentResultEnum.AMOUNT_HAVE_EXCEPTION;
            }
        } else if (enums.getCode().equals(PayWayEnums.CREDIT.getCode()) && param.getCreditMode()
                .equals(CreditModeEnums.CREDIT_ORDER.getCode())) {
            //赊销方式检查
            if (ObjectUtils.isEmpty(creditAmount)) {
                return PaymentResultCode.PaymentResultEnum.CREDIT_AMOUNT_NOT;
            }
            if (ObjectUtils.isEmpty(param.getCreditDays())) {
                return PaymentResultCode.PaymentResultEnum.CREDIT_DAY_NOT;
            }
            if (amount.compareTo(creditAmount.add(payAmount).add(walletAmount)) != 0) {
                return PaymentResultCode.PaymentResultEnum.AMOUNT_HAVE_EXCEPTION;
            }
            if (payAmount.compareTo(BigDecimal.ZERO) > 0) {
                PaymentResultCode.PaymentResultEnum paymentResultEnum = checkVoucher(payAmount, param.getVoucherNo(),
                        param.getVoucherUrls(), isVoucherNo, isVoucherUrl);
                return paymentResultEnum;
            }
        } else if (enums.getCode().equals(PayWayEnums.OFFLINE_TRANSFER.getCode())) {
            //转账方式检查
            if (payAmount.compareTo(amount) != 0) {
                return PaymentResultCode.PaymentResultEnum.AMOUNT_HAVE_EXCEPTION;
            }
            PaymentResultCode.PaymentResultEnum paymentResultEnum = checkVoucher(payAmount, param.getVoucherNo(),
                    param.getVoucherUrls(), isVoucherNo, isVoucherUrl);
            return paymentResultEnum;
        }
        return null;
    }

    /**
     * 线下转账内容检查
     *
     * @param payAmount
     * @param voucherNo
     * @param voucherUrl
     * @return
     */
    private PaymentResultCode.PaymentResultEnum checkVoucher(BigDecimal payAmount, String voucherNo, List<String> voucherUrl,
                                                             Boolean isVoucherNo, Boolean isVoucherUrl) {
        if (ObjectUtils.isEmpty(payAmount)) {
            return PaymentResultCode.PaymentResultEnum.PAY_AMOUNT_NOT;
        }
        if (isVoucherNo && ObjectUtils.isEmpty(voucherNo)) {
            return PaymentResultCode.PaymentResultEnum.VOUCHER_NO_NOT;
        }
        if (isVoucherUrl && CollectionUtils.isEmpty(voucherUrl)) {
            return PaymentResultCode.PaymentResultEnum.VOUCHER_URL_NOT;
        }
        return null;
    }


    /**
     * 赊销额度校验
     *
     * @param companyId
     * @param upCompanyId
     * @param creditAmount
     * @return
     */
    private PaymentResultCode.PaymentResultEnum checkCredit(Integer companyId, Integer upCompanyId, BigDecimal amount,
                                                            BigDecimal payAmount, BigDecimal creditAmount) {
        CreditSettingResp credit = getCredit(companyId, upCompanyId);
        log.info("赊销额度校验 获取赊销设置credit:{}", JSONObject.toJSONString(credit));
        if (ObjectUtils.isEmpty(credit)) {
            return PaymentResultCode.PaymentResultEnum.CREDIT_SETTING_NOT;
        }
        //赊销额度（万元）
        BigDecimal multiply = credit.getCreditLimit().multiply(BigDecimal.valueOf(10000));
        if (multiply.subtract(credit.getCreditUse()).compareTo(creditAmount) < 0) {
            return PaymentResultCode.PaymentResultEnum.CREDIT_AMOUNT_SHORTAGE;
        }
        BigDecimal minAmount = credit.getPayPercent().multiply(amount).setScale(6);
        if (payAmount.compareTo(minAmount) < 0) {
            return PaymentResultCode.PaymentResultEnum.CREDIT_AMOUNT_LT_MIN;
        }
        return null;
    }

    /**
     * 校验收款信息
     *
     * @param orderNos
     * @return
     */
    private PaymentResultCode.PaymentResultEnum checkReceipt(List<String> orderNos) {
        if (CollectionUtils.isEmpty(orderNos)) {
            return PaymentResultCode.PaymentResultEnum.ORDER_NOT_EXISTS;
        }
        return null;
    }

    /**
     * 获取余额账户
     *
     * @param companyId
     * @param upCompanyId
     * @return AccountBalanceResp
     */
    private Map<Integer, AccountBalanceResp> getAccountBalance(Integer companyId, Integer upCompanyId, Integer sourceCompanyId, Integer toCompanyId) {
        AccountBalanceReq req = new AccountBalanceReq();
        req.setCompanyId(companyId);
        req.setUpCompanyId(upCompanyId);
        req.setToCompanyId(toCompanyId);
        req.setSourceCompanyId(sourceCompanyId);
        AccountCashierResp resp = balanceFeignClient.findByCompanyId(req);
        Map<Integer, AccountBalanceResp> map = resp.getAccountList().stream().collect(Collectors.toMap(AccountBalanceResp::getAccountType, r -> r));
        return map;
    }


    /**
     * 获取收款单信息
     */
    private ReceiptOnlineResp getReceiptByNos(AcquireReceiptOnlineReq req) {
        Result<ReceiptOnlineResp> onlineRespResult = receiptOrderFeignClient.acquireReceiptOnline(req);
        if (onlineRespResult.getCode() != 0) {
            throw new BusinessException(onlineRespResult.getCode(), onlineRespResult.getMessage());
        }
        ReceiptOnlineResp data = onlineRespResult.getData();
        return data;
    }

    /**
     * 获取赊销设置
     *
     * @param companyId
     * @param upCompanyId
     * @return
     */
    private CreditSettingResp getCredit(Integer companyId, Integer upCompanyId) {
        CreditSettingReq creditSettingReq = new CreditSettingReq();
        creditSettingReq.setCompanyId(companyId);
        creditSettingReq.setUpCompanyId(upCompanyId);
        Result<CreditSettingResp> creditResp = creditSettingFeignClient.findByCompanyId(creditSettingReq);
        CreditSettingResp credit = creditResp.getData();
        return credit;
    }

    /**
     * 获取赊销设置
     *
     * @param payCompanyId
     * @param receiptCompanyId
     * @return
     */
    private CreditConfigureResp getCreditConfigure(Integer payCompanyId, Integer receiptCompanyId) {
        QueryCreditReq req = new QueryCreditReq();
        req.setCompanyId(receiptCompanyId);
        req.setCreditCompanyId(payCompanyId);
        Result<CreditConfigureResp> credit = creditConfigureFeignClient.getCreditConfigureByCompanyId(req);
        return credit.getData();
    }

    /**
     * 收款流水记录
     *
     * @param req
     * @return
     */
    private ReceiptOnlineResp queryReceiptOnline(ReceiptOnlineReq req) {
        Result<ReceiptOnlineResp> onlineRespResult = receiptOrderFeignClient.queryReceiptOnline(req);
        ReceiptOnlineResp data = onlineRespResult.getData();
        return data;
    }

    /**
     * 获取应用信息
     *
     * @param cmd
     * @return
     */
    private IPayAppInfo getByCmd(String cmd, String tenantId) {
        IPayAppInfo appInfo = appInfoManager.getOne(new LambdaQueryWrapper<IPayAppInfo>()
                .eq(BaseModel::getTenantId, tenantId)
                .eq(IPayAppInfo::getGrantCmd, cmd)
                .eq(IPayAppInfo::getIfAuth, true)
                .eq(IPayAppInfo::getIfUse, true));
        if (ObjectUtils.isEmpty(appInfo)) {
            appInfo = new IPayAppInfo();
            appInfo.setIfUse(true);
            appInfo.setIfAuth(true);
            appInfo.setGrantCmd(cmd);
            appInfo.setTenantId(AppThreadLocal.getTenantId());
            appInfoManager.saveOrUpdate(appInfo);
        }
        return appInfo;
    }

    /**
     * apiV3支付(online)
     * apiV3Pay
     *
     * @param param
     * @return
     */
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public Result<OnlinePayParametersVo> apiV3Pay(HttpServletRequest request, UnifiedPayParam param, ReceiptOnlineResp receiptOrder) {
        log.info("apiV3Pay支付快捷支付参数 param:{}, receiptOrder:{}", JSONObject.toJSONString(param), JSONObject.toJSONString(receiptOrder));
        RequestUser user = RequestUserUtils.getUser();
        OnlinePayParametersVo parametersVo = new OnlinePayParametersVo();
        parametersVo.setPayChannel(PayWayEnums.WECHAT_PAY.toString());
        parametersVo.setPayResult(PayResultEnums.PAY_UNPAID.getCode());
        MerchantSub sub = subManager.getOne(new LambdaQueryWrapper<MerchantSub>()
                .eq(BaseModel::getTenantId, user.getTenantId())
        );
        List<MerchantInfo> list = merchantInfoManager.list(new LambdaQueryWrapper<MerchantInfo>()
                .eq(MerchantInfo::getId, sub.getMerchantId())
        );
        MerchantInfo merchantInfo = list.get(0);
        if (ObjectUtils.isEmpty(sub)) {
            throw new BusinessException(20004, "该店铺未开通小程序店铺");
        }
        //微信小程序
        String ip = IpUtil.api.getIpAddress(request);
        WeChatPayParam wechatPayParam = new WeChatPayParam();
        wechatPayParam.setIp(ip);
        wechatPayParam.setProductName(receiptOrder.getTitle());
        wechatPayParam.setBusinessNo(receiptOrder.getPayOrderNo());
        wechatPayParam.setPrice(receiptOrder.getAmount());
        wechatPayParam.setCode(param.getCode());
        WechatCallPayParametersVO vo = wechatPayService.appletApiV3Pay(wechatPayParam, merchantInfo, sub);
        parametersVo.setWechatPayParam(vo);
        return Result.build(parametersVo);
    }

    /**
     * 统一支付（自生成二维码支付）
     *
     * @param request
     * @param response
     * @param payOrderNo
     * @param payUserId
     * @param tenantId
     * @throws IOException
     */
    @Transactional(readOnly = false)
    public void unifiedQrCodePay(HttpServletRequest request, HttpServletResponse response, String payOrderNo, Integer payUserId,
                                 String tenantId) throws IOException {
        log.info("统一支付聚合二维码支付参数payOrderNo:{}", payOrderNo);
        AppThreadLocal.setTenantId(tenantId);
        AppThreadLocal.setSourceCode("hsj");
        String userAgent = request.getHeader("user-agent");
        ReceiptOnlineReq req = new ReceiptOnlineReq();
        req.setPayOrderNo(payOrderNo);
        ReceiptOnlineResp resp = queryReceiptOnline(req);
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        log.info("收款记录流水receiptOrderResp:{}", JSONObject.toJSONString(resp));
        if (ObjectUtils.isEmpty(resp)) {
            response.getWriter().write("未查到付款信息");
            return;
        }
        String ip = IpUtil.api.getIpAddress(request);
        SupportUnifiedPayDTO dto = new SupportUnifiedPayDTO();
        dto.setClientIp(ip);
        dto.setPayOrderNo(payOrderNo);
        dto.setPayUserId(payUserId);
        dto.setUserAgent(userAgent);
        dto.setTenantId(tenantId);
        dto.setDomain(DomainEnums.PC_DOMAIN.getCode());
        if (ObjectUtils.isNotNull(userAgent) && userAgent.contains("MicroMessenger")) {
            dto.setPayWay(PayWayEnums.WECHAT_PAY.getCode());
        }
        if (ObjectUtils.isNotNull(userAgent) && userAgent.contains("AlipayClient")) {
            dto.setPayWay(PayWayEnums.ALI_PAY.getCode());
        }
        List<SupportPaymentPayWayDTO> payWayListByRoute = paymentPayWayManager.getPayWayListByRoute(tenantId);
        if (CollectionUtils.isNotEmpty(payWayListByRoute)) {
            SupportPaymentPayWayDTO payWayDTO = payWayListByRoute.stream().filter(r -> r.getCode().equals(dto.getPayWay()))
                    .findFirst().orElse(null);
            if (ObjectUtils.isNull(payWayDTO)) {
                throw new BusinessException(80071, "商户未开通相关支付方式或支付渠道！");
            }
            ChannelRouteInterfaceTypeEnums interfaceEnum = ChannelRouteInterfaceTypeEnums.getByInterfaceCode(payWayDTO.getInterfaceCode());
            IPayUnifiedPayStrategy unifiedPayStrategy = unifiedPayStrategyMap.get(interfaceEnum);
            dto.setChannelId(payWayDTO.getChannelId());
            dto.setPayMode(payWayDTO.getChannelCode());
            SupportUnifiedQrPayDTO payDTO = unifiedPayStrategy.unifiedQrPay(dto, resp);
            log.info("统一支付聚合二维码支付codeUrl:{}", JSONObject.toJSONString(payDTO));
            response.sendRedirect(payDTO.getPayInfoUrl());
        }

    }

}